/***************************************************************/
/*                                                             */
/*   LC-3b Simulator                                           */
/*                                                             */
/*   Name: Renato Oliveira                                     */
/*   UTEID : rad2735                                           */
/*   EE 460N                                                   */
/*   The University of Texas at Austin                         */
/*                                                             */
/***************************************************************/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/***************************************************************/
/*                                                             */
/* Files:  ucode        Microprogram file                      */
/*         isaprogram   LC-3b machine language program file    */
/*                                                             */
/***************************************************************/

/***************************************************************/
/* These are the functions you'll have to write.               */
/***************************************************************/

void eval_micro_sequencer();
void cycle_memory();
void eval_bus_drivers();
void drive_bus();
void latch_datapath_values();

/***************************************************************/
/* A couple of useful definitions.                             */
/***************************************************************/
#define FALSE 0
#define TRUE  1

/***************************************************************/
/* Use this to avoid overflowing 16 bits on the bus.           */
/***************************************************************/
#define Low16bits(x) ((x) & 0xFFFF)

/***************************************************************/
/* Definition of the control store layout.                     */
/***************************************************************/
#define CONTROL_STORE_ROWS 128
#define INITIAL_STATE_NUMBER 18

/***************************************************************/
/* Definition of bit order in control store word.              */
/***************************************************************/
enum CS_BITS {
  IRD,
  COND2, COND1, COND0,   /*New COND2 singal*/
  J6, J5, J4, J3, J2, J1, J0,
  LD_MAR,
  LD_MDR,
  LD_IR,
  LD_BEN,
  LD_REG,
  LD_CC,
  LD_PC,
  LD_Priv,
  LD_Prio,
  LD_SavedSSP,
  LD_SavedUSP,
  LD_Vector,
  GATE_PC,
  GATE_MDR,
  GATE_ALU,
  GATE_MARMUX,
  GATE_Vector,
  GATE_PCmin2,
  GATE_PSR,
  GATE_SP,
  GATE_SHF,
  PCMUX1, PCMUX0,
  DRMUX1, DRMUX0,
  SR1MUX1, SR1MUX0,
  ADDR1MUX,
  ADDR2MUX1, ADDR2MUX0,
  SPMUX1, SPMUX0,
  MARMUX,
  VectorMUX1, VectorMUX0,
  PSRMUX,
  ALUK1, ALUK0,
  MIO_EN,
  R_W,
  Set_Priv,
  DATA_SIZE,
  LSHF1,
  /* MODIFY: you have to add all your new control signals */
  CONTROL_STORE_BITS
} CS_BITS;

/***************************************************************/
/* Functions to get at the control bits.                       */
/***************************************************************/
int GetIRD(int *x)           { return(x[IRD]); }
int GetCOND(int *x)          { return((x[COND2] << 2) + (x[COND1] << 1) + (x[COND0])); } /*New COND2 singal*/
int GetJ(int *x)             { return((x[J6] << 6) + (x[J5] << 5) + (x[J4] << 4) + 
                                      (x[J3] << 3) + (x[J2] << 2) +
                                      (x[J1] << 1) + x[J0]);}
int GetLD_MAR(int *x)        { return(x[LD_MAR]); }
int GetLD_MDR(int *x)        { return(x[LD_MDR]); }
int GetLD_IR(int *x)         { return(x[LD_IR]); }
int GetLD_BEN(int *x)        { return(x[LD_BEN]); }
int GetLD_REG(int *x)        { return(x[LD_REG]); }
int GetLD_CC(int *x)         { return(x[LD_CC]); }
int GetLD_PC(int *x)         { return(x[LD_PC]); }
int GetLD_Priv(int *x)       { return(x[LD_Priv]); }
int GetLD_Prio(int *x)       { return(x[LD_Prio]); }
int GetLD_SavSSP(int *x)     { return(x[LD_SavedSSP]); }
int GetLD_SavUSP(int *x)     { return(x[LD_SavedUSP]); }
int GetLD_Vector(int *x)     { return(x[LD_Vector]); }
int GetGATE_PC(int *x)       { return(x[GATE_PC]); }
int GetGATE_MDR(int *x)      { return(x[GATE_MDR]); }
int GetGATE_ALU(int *x)      { return(x[GATE_ALU]); }
int GetGATE_MARMUX(int *x)   { return(x[GATE_MARMUX]); }
int GetGATE_Vector(int *x)   { return(x[GATE_Vector]); }
int GetGATE_PCmin2(int *x)   { return(x[GATE_PCmin2]); }
int GetGATE_PSR(int *x)      { return(x[GATE_PSR]); }
int GetGATE_SP(int *x)       { return(x[GATE_SP]); }
int GetGATE_SHF(int *x)      { return(x[GATE_SHF]); }
int GetPCMUX(int *x)         { return((x[PCMUX1] << 1)  + x[PCMUX0]);  }
int GetDRMUX(int *x)         { return((x[DRMUX1] << 1)  + x[DRMUX0]);  }
int GetSR1MUX(int *x)        { return((x[SR1MUX1] << 1) + x[SR1MUX0]); }
int GetADDR1MUX(int *x)      { return(x[ADDR1MUX]); }
int GetADDR2MUX(int *x)      { return((x[ADDR2MUX1] << 1) + x[ADDR2MUX0]); }
int GetSPMUX(int *x)         { return((x[SPMUX1] << 1) + x[SPMUX0]); }
int GetMARMUX(int *x)        { return(x[MARMUX]); }
int GetVectorMUX(int *x)     { return((x[VectorMUX1] << 1) + x[VectorMUX0]); }
int GetPSRMUX(int *x)        { return(x[PSRMUX]); }
int GetALUK(int *x)          { return((x[ALUK1] << 1) + x[ALUK0]); }
int GetMIO_EN(int *x)        { return(x[MIO_EN]); }
int GetR_W(int *x)           { return(x[R_W]); }
int GetSetPriv(int *x)       { return(x[Set_Priv]); }
int GetDATA_SIZE(int *x)     { return(x[DATA_SIZE]); }
int GetLSHF1(int *x)         { return(x[LSHF1]); }
/* MODIFY: you can add more Get functions for your new control signals */

/***************************************************************/
/* The control store rom.                                      */
/***************************************************************/
int CONTROL_STORE[CONTROL_STORE_ROWS][CONTROL_STORE_BITS];

/***************************************************************/
/* Main memory.                                                */
/***************************************************************/
/* MEMORY[A][0] stores the least significant byte of word at word address A
   MEMORY[A][1] stores the most significant byte of word at word address A
   There are two write enable signals, one for each byte. WE0 is used for
   the least significant byte of a word. WE1 is used for the most significant
   byte of a word. */

#define WORDS_IN_MEM    0x08000 
#define MEM_CYCLES      5
int MEMORY[WORDS_IN_MEM][2];

/***************************************************************/

/***************************************************************/

/***************************************************************/
/* LC-3b State info.                                           */
/***************************************************************/
#define LC_3b_REGS 8

int RUN_BIT;	/* run bit */
int BUS;	/* value of the bus */

typedef struct System_Latches_Struct{

  int PC,		  /* program counter */
      PSR,    /*processor status register*/
      MDR,	  /* memory data register */
      MAR,	  /* memory address register */
      IR,		  /* instruction register */
      N,		  /* n condition bit */
      Z,		  /* z condition bit */
      P,		  /* p condition bit */
      BEN;    /* ben register */

  int READY;	/* ready bit */
  /* The ready bit is also latched as you dont want the memory system to assert it
     at a bad point in the cycle*/

  int REGS[LC_3b_REGS]; /* register file. */

  int MICROINSTRUCTION[CONTROL_STORE_BITS]; /* The microintruction */

  int STATE_NUMBER; /* Current State Number - Provided for debugging */

  /* For lab 4 */
  int INTV; /* Interrupt vector register */
  int EXCV; /* Exception vector register */
  int SSP;  /* Initial value of system stack pointer */

  /* MODIFY: You may add system latches that are required by your implementation */
  int priority, /*Program priority*/
      Priv,     /*Program privilege level*/
      Vector,   /*register to store the vector address*/
      USP;      /*register to store the stack pointer stuf*/


} System_Latches;

/* Data Structure for Latch */

System_Latches CURRENT_LATCHES, NEXT_LATCHES;

/***************************************************************/
/* A cycle counter.                                            */
/***************************************************************/
int CYCLE_COUNT;

/*interupt signal*/
int INT_SIG = 0;
int INT_PRIO = 1;

/***************************************************************/
/*                                                             */
/* Procedure : help                                            */
/*                                                             */
/* Purpose   : Print out a list of commands.                   */
/*                                                             */
/***************************************************************/
void help() {
  printf("----------------LC-3bSIM Help-------------------------\n");
  printf("go               -  run program to completion       \n");
  printf("run n            -  execute program for n cycles    \n");
  printf("mdump low high   -  dump memory from low to high    \n");
  printf("rdump            -  dump the register & bus values  \n");
  printf("?                -  display this help menu          \n");
  printf("quit             -  exit the program                \n\n");
}

/***************************************************************/
/*                                                             */
/* Procedure : cycle                                           */
/*                                                             */
/* Purpose   : Execute a cycle                                 */
/*                                                             */
/***************************************************************/
void cycle() {

  eval_micro_sequencer();
  cycle_memory();
  eval_bus_drivers();
  drive_bus();
  latch_datapath_values();

  CURRENT_LATCHES = NEXT_LATCHES;

  CYCLE_COUNT++;

  /*trigger interupt*/
  if (CYCLE_COUNT == 300)
    INT_SIG = 1;
}

/***************************************************************/
/*                                                             */
/* Procedure : run n                                           */
/*                                                             */
/* Purpose   : Simulate the LC-3b for n cycles.                 */
/*                                                             */
/***************************************************************/
void run(int num_cycles) {
  int i;

  if (RUN_BIT == FALSE) {
    printf("Can't simulate, Simulator is halted\n\n");
    return;
  }

  printf("Simulating for %d cycles...\n\n", num_cycles);
  for (i = 0; i < num_cycles; i++) {
    if (CURRENT_LATCHES.PC == 0x0000) {
      RUN_BIT = FALSE;
      printf("Simulator halted\n\n");
      break;
    }
    cycle();
  }
}

/***************************************************************/
/*                                                             */
/* Procedure : go                                              */
/*                                                             */
/* Purpose   : Simulate the LC-3b until HALTed.                 */
/*                                                             */
/***************************************************************/
void go() {
  if (RUN_BIT == FALSE) {
    printf("Can't simulate, Simulator is halted\n\n");
    return;
  }

  printf("Simulating...\n\n");
  while (CURRENT_LATCHES.PC != 0x0000)
    cycle();
  RUN_BIT = FALSE;
  printf("Simulator halted\n\n");
}

/***************************************************************/
/*                                                             */
/* Procedure : mdump                                           */
/*                                                             */
/* Purpose   : Dump a word-aligned region of memory to the     */
/*             output file.                                    */
/*                                                             */
/***************************************************************/
void mdump(FILE * dumpsim_file, int start, int stop) {
  int address; /* this is a byte address */

  printf("\nMemory content [0x%0.4x..0x%0.4x] :\n", start, stop);
  printf("-------------------------------------\n");
  for (address = (start >> 1); address <= (stop >> 1); address++)
    printf("  0x%0.4x (%d) : 0x%0.2x%0.2x\n", address << 1, address << 1, MEMORY[address][1], MEMORY[address][0]);
  printf("\n");

  /* dump the memory contents into the dumpsim file */
  fprintf(dumpsim_file, "\nMemory content [0x%0.4x..0x%0.4x] :\n", start, stop);
  fprintf(dumpsim_file, "-------------------------------------\n");
  for (address = (start >> 1); address <= (stop >> 1); address++)
    fprintf(dumpsim_file, " 0x%0.4x (%d) : 0x%0.2x%0.2x\n", address << 1, address << 1, MEMORY[address][1], MEMORY[address][0]);
  fprintf(dumpsim_file, "\n");
  fflush(dumpsim_file);
}

/***************************************************************/
/*                                                             */
/* Procedure : rdump                                           */
/*                                                             */
/* Purpose   : Dump current register and bus values to the     */
/*             output file.                                    */
/*                                                             */
/***************************************************************/
void rdump(FILE * dumpsim_file) {
  int k;

  printf("\nCurrent register/bus values :\n");
  printf("-------------------------------------\n");
  printf("Cycle Count  : %d\n", CYCLE_COUNT);
  printf("PC           : 0x%0.4x\n", CURRENT_LATCHES.PC);
  printf("IR           : 0x%0.4x\n", CURRENT_LATCHES.IR);
  printf("STATE_NUMBER : 0x%0.4x\n\n", CURRENT_LATCHES.STATE_NUMBER);
  printf("BUS          : 0x%0.4x\n", BUS);
  printf("MDR          : 0x%0.4x\n", CURRENT_LATCHES.MDR);
  printf("MAR          : 0x%0.4x\n", CURRENT_LATCHES.MAR);
  printf("CCs: N = %d  Z = %d  P = %d\n", CURRENT_LATCHES.N, CURRENT_LATCHES.Z, CURRENT_LATCHES.P);
  printf("Registers:\n");
  for (k = 0; k < LC_3b_REGS; k++)
    printf("%d: 0x%0.4x\n", k, CURRENT_LATCHES.REGS[k]);
  printf("\n");

  /* dump the state information into the dumpsim file */
  fprintf(dumpsim_file, "\nCurrent register/bus values :\n");
  fprintf(dumpsim_file, "-------------------------------------\n");
  fprintf(dumpsim_file, "Cycle Count  : %d\n", CYCLE_COUNT);
  fprintf(dumpsim_file, "PC           : 0x%0.4x\n", CURRENT_LATCHES.PC);
  fprintf(dumpsim_file, "IR           : 0x%0.4x\n", CURRENT_LATCHES.IR);
  fprintf(dumpsim_file, "STATE_NUMBER : 0x%0.4x\n\n", CURRENT_LATCHES.STATE_NUMBER);
  fprintf(dumpsim_file, "BUS          : 0x%0.4x\n", BUS);
  fprintf(dumpsim_file, "MDR          : 0x%0.4x\n", CURRENT_LATCHES.MDR);
  fprintf(dumpsim_file, "MAR          : 0x%0.4x\n", CURRENT_LATCHES.MAR);
  fprintf(dumpsim_file, "CCs: N = %d  Z = %d  P = %d\n", CURRENT_LATCHES.N, CURRENT_LATCHES.Z, CURRENT_LATCHES.P);
  fprintf(dumpsim_file, "Registers:\n");
  for (k = 0; k < LC_3b_REGS; k++)
    fprintf(dumpsim_file, "%d: 0x%0.4x\n", k, CURRENT_LATCHES.REGS[k]);
  fprintf(dumpsim_file, "\n");
  fflush(dumpsim_file);
}

/***************************************************************/
/*                                                             */
/* Procedure : get_command                                     */
/*                                                             */
/* Purpose   : Read a command from standard input.             */
/*                                                             */
/***************************************************************/
void get_command(FILE * dumpsim_file) {
  char buffer[20];
  int start, stop, cycles;

  printf("LC-3b-SIM> ");

  scanf("%s", buffer);
  printf("\n");

  switch (buffer[0]) {
  case 'G':
  case 'g':
    go();
    break;

  case 'M':
  case 'm':
    scanf("%i %i", &start, &stop);
    mdump(dumpsim_file, start, stop);
    break;

  case '?':
    help();
    break;
  case 'Q':
  case 'q':
    printf("Bye.\n");
    exit(0);

  case 'R':
  case 'r':
    if (buffer[1] == 'd' || buffer[1] == 'D')
      rdump(dumpsim_file);
    else {
      scanf("%d", &cycles);
      run(cycles);
    }
    break;

  default:
    printf("Invalid Command\n");
    break;
  }
}

/***************************************************************/
/*                                                             */
/* Procedure : init_control_store                              */
/*                                                             */
/* Purpose   : Load microprogram into control store ROM        */
/*                                                             */
/***************************************************************/
void init_control_store(char *ucode_filename) {
  FILE *ucode;
  int i, j, index;
  char line[200];

  printf("Loading Control Store from file: %s\n", ucode_filename);

  /* Open the micro-code file. */
  if ((ucode = fopen(ucode_filename, "r")) == NULL) {
    printf("Error: Can't open micro-code file %s\n", ucode_filename);
    exit(-1);
  }

  /* Read a line for each row in the control store. */
  for (i = 0; i < CONTROL_STORE_ROWS; i++) {
    if (fscanf(ucode, "%[^\n]\n", line) == EOF) {
      printf("Error: Too few lines (%d) in micro-code file: %s\n",
        i, ucode_filename);
      exit(-1);
    }

    /* Put in bits one at a time. */
    index = 0;

    for (j = 0; j < CONTROL_STORE_BITS; j++) {
      /* Needs to find enough bits in line. */
      if (line[index] == '\0') {
        printf("Error: Too few control bits in micro-code file: %s\nLine: %d\n",
          ucode_filename, i);
        exit(-1);
      }
      if (line[index] != '0' && line[index] != '1') {
        printf("Error: Unknown value in micro-code file: %s\nLine: %d, Bit: %d\n",
          ucode_filename, i, j);
        exit(-1);
      }

      /* Set the bit in the Control Store. */
      CONTROL_STORE[i][j] = (line[index] == '0') ? 0 : 1;
      index++;
    }

    /* Warn about extra bits in line. */
    if (line[index] != '\0')
      printf("Warning: Extra bit(s) in control store file %s. Line: %d\n",
      ucode_filename, i);
  }
  printf("\n");
}

/************************************************************/
/*                                                          */
/* Procedure : init_memory                                  */
/*                                                          */
/* Purpose   : Zero out the memory array                    */
/*                                                          */
/************************************************************/
void init_memory() {
  int i;

  for (i = 0; i < WORDS_IN_MEM; i++) {
    MEMORY[i][0] = 0;
    MEMORY[i][1] = 0;
  }
}

/**************************************************************/
/*                                                            */
/* Procedure : load_program                                   */
/*                                                            */
/* Purpose   : Load program and service routines into mem.    */
/*                                                            */
/**************************************************************/
void load_program(char *program_filename) {
  FILE * prog;
  int ii, word, program_base;

  /* Open program file. */
  prog = fopen(program_filename, "r");
  if (prog == NULL) {
    printf("Error: Can't open program file %s\n", program_filename);
    exit(-1);
  }

  /* Read in the program. */
  if (fscanf(prog, "%x\n", &word) != EOF)
    program_base = word >> 1;
  else {
    printf("Error: Program file is empty\n");
    exit(-1);
  }

  ii = 0;
  while (fscanf(prog, "%x\n", &word) != EOF) {
    /* Make sure it fits. */
    if (program_base + ii >= WORDS_IN_MEM) {
      printf("Error: Program file %s is too long to fit in memory. %x\n",
        program_filename, ii);
      exit(-1);
    }

    /* Write the word to memory array. */
    MEMORY[program_base + ii][0] = word & 0x00FF;
    MEMORY[program_base + ii][1] = (word >> 8) & 0x00FF;
    ii++;
  }

  if (CURRENT_LATCHES.PC == 0) CURRENT_LATCHES.PC = (program_base << 1);

  printf("Read %d words from program into memory.\n\n", ii);
}

/***************************************************************/
/*                                                             */
/* Procedure : initialize                                      */
/*                                                             */
/* Purpose   : Load microprogram and machine language program  */
/*             and set up initial state of the machine.        */
/*                                                             */
/***************************************************************/
void initialize(char *ucode_filename, char *program_filename, int num_prog_files) {
  int i;
  init_control_store(ucode_filename);

  init_memory();
  for (i = 0; i < num_prog_files; i++) {
    load_program(program_filename);
    while (*program_filename++ != '\0');
  }
  CURRENT_LATCHES.Z = 1;
  CURRENT_LATCHES.STATE_NUMBER = INITIAL_STATE_NUMBER;
  memcpy(CURRENT_LATCHES.MICROINSTRUCTION, CONTROL_STORE[INITIAL_STATE_NUMBER], sizeof(int)*CONTROL_STORE_BITS);
  CURRENT_LATCHES.SSP = 0x3000; /* Initial value of system stack pointer */
  CURRENT_LATCHES.Priv = 0x01;  /* Initialize the system in user mode*/
  CURRENT_LATCHES.priority = 0x00; /*lowest priority for user program*/

  NEXT_LATCHES = CURRENT_LATCHES;

  RUN_BIT = TRUE;
}

/***************************************************************/
/*                                                             */
/* Procedure : main                                            */
/*                                                             */
/***************************************************************/
int main(int argc, char *argv[]) {
  FILE * dumpsim_file;

  /* Error Checking */
  if (argc < 3) {
    printf("Error: usage: %s <micro_code_file> <program_file_1> <program_file_2> ...\n", argv[0]);
    exit(1);
  }

  printf("LC-3b Simulator\n\n");

  initialize(argv[1], argv[2], argc - 2);

  if ((dumpsim_file = fopen("dumpsim", "w")) == NULL) {
    printf("Error: Can't open dumpsim file\n");
    exit(-1);
  }

  while (1)
    get_command(dumpsim_file);

}

/***************************************************************/
/* Do not modify the above code.
   You are allowed to use the following global variables in your
   code. These are defined above.

   CONTROL_STORE
   MEMORY
   BUS

   CURRENT_LATCHES
   NEXT_LATCHES

   You may define your own local/global variables and functions.
   You may use the functions to get at the control bits defined
   above.

   Begin your code here 	  			       */
/***************************************************************/


#define MEM_CYCLES_NEW MEM_CYCLES - 2

/*global variables */
int PCMUXout      = 0;
int ALUout        = 0;
int SHFout        = 0;
int MARMUXout     = 0;
int MDRout        = 0;
int ADDEROut      = 0;
int SPMUXout      = 0;
int CondCodes     = 0;
int MEMORYout     = 0;
int MEMORYin      = 0;

/*memory cycle counter to keep track of five memory cycles*/
int MEMEN = 0;

/*global access to Register*/
int SR2 = 0;
int SR1 = 0;

int signExtension(int value, int size){
  int result = 0;
  int i = 1;
  int bits = 1;
  int mask = 1;

  /*build the mask according the the bit size*/
  for (i = 1; i < size; i++) {
    bits = bits << 1;
    bits += 1;
  }
  result = (bits & value);
  mask = mask << (size - 1);

  if (mask & result) {
    result += ~bits;
  }

  return result;
}

int processALU(int a, int b, int aluk) {

  int result = 0;

  switch (aluk) {
    case 0:
    {
      result = Low16bits(signExtension(a + b, 16));
      break;
    }
    case 1:
    {
      result = Low16bits(signExtension(a & b, 16));
      break;
    }
    case 2:
    {
      result = Low16bits(signExtension(a ^ b, 16));
      break;
    }
    case 3: {
      result = Low16bits(signExtension(a, 16));
      break;
    }
  }

  return result;
}

int processMUX(int input0, int input1, int input2, int input3, int select){

  int result = 0;

  switch (select) {
  case 0:
  {
    result = input0;
    break;
  }
  case 1:
  {
    result = input1;
    break;
  }
  case 2:
  {
    result = input2;
    break;
  }
  case 3:
  {
    result = input3;
    break;
  }
  }
  return result;
}

int processSHF(int in, int LA_amount) {
  int result = 0;

  int amount = LA_amount & 0x0F;
  int direction = (LA_amount & 0x0030) >> 4;

  switch (direction) {
    case 0: {
      /*logical left shift*/
      result = Low16bits(in << amount);
      break;
    }
    case 1: {
      /*logigal right shift*/
      result = Low16bits(in >> amount);
      break;
    }
    case 3: {
      /*arithmetic right shift*/
      result = Low16bits(signExtension(in, 16) >> amount);
      break;
    }
  }

  return result;
}

void eval_micro_sequencer() {

  /*
   * Evaluate the address of the next state according to the
   * micro sequencer logic. Latch the next microinstruction.
   */
  /*get the required bits for to process the next state*/
  int mio         = GetMIO_EN(CURRENT_LATCHES.MICROINSTRUCTION);
  int ird         = GetIRD(CURRENT_LATCHES.MICROINSTRUCTION);
  int cond        = GetCOND(CURRENT_LATCHES.MICROINSTRUCTION);
  int j           = GetJ(CURRENT_LATCHES.MICROINSTRUCTION);
  int r           = CURRENT_LATCHES.READY;
  int ben         = CURRENT_LATCHES.BEN;
  int instruction = (CURRENT_LATCHES.IR & 0x0000F800) >> 11; /*extract IR[15:11]*/
  int ir_11       = instruction & 0x01;
  int ir_15_12    = (instruction & 0x1E) >> 1;

  /*interupt variables*/
  int psr_15      = (CURRENT_LATCHES.Priv);
  int INT         = 0;
  int protect     = 0;
  int prioA       = 0;
  int prioB       = 0;

  /*next state variables*/
  int j0 = 0, j1 = 0, j2 = 0, j3 = 0, j4 = 0, j5 = 0, j6 = 0, nextj = 0;
  int cond0 = cond & 0x01;
  int cond1 = (cond >> 1) & 0x01;
  int cond2 = (cond >> 2) & 0x01;
  
  /*check protection exception*/
  if ((CURRENT_LATCHES.STATE_NUMBER == 62) ||
      (CURRENT_LATCHES.STATE_NUMBER == 56) ||
      (CURRENT_LATCHES.STATE_NUMBER == 57) ||
      (CURRENT_LATCHES.STATE_NUMBER == 63)) {
  
    if (CURRENT_LATCHES.MAR < 0x3000 && CURRENT_LATCHES.MAR >= 0x0000 && psr_15 == 1) {
      protect = 1;
    }
    else if (((CURRENT_LATCHES.STATE_NUMBER == 56) ||
             (CURRENT_LATCHES.STATE_NUMBER == 57)) &&
             ((CURRENT_LATCHES.MAR & 0x01) == 1)) {
      nextj = 46;
      goto done;

    }

  }

  /*check for the INT signal*/
  if (INT_SIG == 1 && CURRENT_LATCHES.STATE_NUMBER == 18) {
    prioA = (INT_PRIO & 0x0007);
    prioB = (CURRENT_LATCHES.priority & 0x0007);
    INT = (prioA > prioB) ? 1 : 0;

    /*acknoledge interupt*/
    INT_SIG = 0;
  }


  j0 = j & 0x01;
  j1 = (j >> 1) & 0x01;
  j2 = (j >> 2) & 0x01;
  j3 = (j >> 3) & 0x01;
  j4 = (j >> 4) & 0x01;
  j5 = (j >> 5) & 0x01;
  j6 = (j >> 6) & 0x01;

  if (ird) {
    /*set next state based on opcodes*/
    nextj |= ir_15_12;
  }
  else {
    /*set next state based on COND, J, and IR[11]*/
    j0 = j0 | (ir_11 & cond0 & cond1 & (~cond2 & 0x01));
    j1 = j1 | (r & cond0 & (~cond1 & 0x01) & (~cond2 & 0x01));
    j2 = j2 | (ben & (~cond0 & 0x01) & cond1 & (~cond2 & 0x01));
    j3 = j3 | (psr_15 & (~cond0 & 0x01) & (~cond1 & 0x01) & cond2);
    j4 = j4 | (INT & cond0 & (~cond1 & 0x01) & cond2);
    
    j6 = j6 | (protect & (~cond0 & 0x01) & cond1 & cond2);

    nextj = (j6 << 6) + (j5 << 5) + (j4 << 4) + (j3 << 3) + (j2 << 2) + (j1 << 1) + j0;
  }

done:
  /*set the next state microinstruction based on the calculated adddress*/
  memcpy(NEXT_LATCHES.MICROINSTRUCTION, CONTROL_STORE[nextj], sizeof(int)*CONTROL_STORE_BITS);
  NEXT_LATCHES.STATE_NUMBER = nextj;

}

void cycle_memory() {

  /*
   * This function emulates memory and the WE logic.
   * Keep track of which cycle of MEMEN we are dealing with.
   * If fourth, we need to latch Ready bit at the end of
   * cycle to prepare microsequencer for the fifth cycle.
   */
  int datasize = GetDATA_SIZE(CURRENT_LATCHES.MICROINSTRUCTION);
  int mio      = GetMIO_EN(CURRENT_LATCHES.MICROINSTRUCTION);
  int RW       = GetR_W(CURRENT_LATCHES.MICROINSTRUCTION);
  int mar      = CURRENT_LATCHES.MAR;
  int mar0     = mar & 0x01;
  int mdr      = CURRENT_LATCHES.MDR;
  
  /*variables*/
  int MAR0MUXout = 0;

  if (mio) {
    /*keep track of how many memory cycles occurred*/
    if (MEMEN < MEM_CYCLES_NEW) {
      MEMEN++;
      NEXT_LATCHES.READY = 0;
    }
    else {
      /*latch the READY bit on the 4th cycle so
      that the microsequencer uses it in the 5th cycle*/
      if (MEMEN == MEM_CYCLES_NEW) {
        MEMEN++;
        NEXT_LATCHES.READY = 1;
      }
      else {
        /*Reset Mem Counter exactly at 5th cycle*/
        MEMEN = 0;
        NEXT_LATCHES.READY = 0;
      }
    /*perform the memory cycle*/
      if (RW) {
        if (datasize == 0) {
          if (mar0 == 0) {
            MEMORY[mar / 2][0] = mdr & 0x00FF;
          }
          else {
            MEMORY[mar / 2][1] = (mdr & 0xFF00) >> 8;
          }
        }
        else {
          if (mar0 == 0) {
            MEMORY[mar / 2][0] = 0x00FF & mdr;
            MEMORY[mar / 2][1] = 0x00FF & ((mdr & 0xFF00) >> 8);
          }
        }
      }
      else {
        /*place the output of memory ready to load into MDR*/
        MEMORYout = Low16bits(((MEMORY[mar / 2][1] & 0x00FF) << 8) | (MEMORY[mar / 2][0] & 0x00FF));
      }
    }
  }
}

void eval_bus_drivers() {

  /*
   * Datapath routine emulating operations before driving the bus.
   * Evaluate the input of tristate drivers
   *         Gate_MARMUX,
   *		 Gate_PC,
   *		 Gate_ALU,
   *		 Gate_SHF,
   *		 Gate_MDR.
   */
  /*check what are the select lines for the Muxes*/
  int aluk     = GetALUK(CURRENT_LATCHES.MICROINSTRUCTION);
  int marmux   = GetMARMUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int addr1mux = GetADDR1MUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int addr2mux = GetADDR2MUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int datasize = GetDATA_SIZE(CURRENT_LATCHES.MICROINSTRUCTION);
  int sr1mux   = GetSR1MUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int spmux    = GetSPMUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int mio      = GetMIO_EN(CURRENT_LATCHES.MICROINSTRUCTION);


  /*get load signals*/
  int lshf1 = GetLSHF1(CURRENT_LATCHES.MICROINSTRUCTION);

  /*get stack pointer information*/
  int savssp = CURRENT_LATCHES.SSP;
  int savusp = CURRENT_LATCHES.USP;

  /*get Source register and etc*/
  int Ready = CURRENT_LATCHES.READY;
  int mdr = CURRENT_LATCHES.MDR;
  int mar = CURRENT_LATCHES.MAR;
  int ir = CURRENT_LATCHES.IR;
  int A = (ir & 0x0020) >> 5;
  int SR2MUXout = 0;
  int ADDR1MUXout = 0;
  int ADDR2MUXout = 0;
  int MAR0MUXout = 0;

  /*get the current source registers*/
  SR2 = ir & 0x0007;
  SR1 = processMUX((ir & 0x0E00) >> 9,( ir & 0x01C0) >> 6, 0x06, 0, sr1mux);

  /*do nothing while  performing mem cycle*/
  if ((mio == 1) && (CURRENT_LATCHES.READY == 0)) return;

  /*process MDR gate input*/
  MAR0MUXout = processMUX(mdr & 0x00FF, (mdr & 0xFF00) >> 8, 0, 0, mar & 0x01);
  MAR0MUXout = Low16bits(signExtension(MAR0MUXout, 8));
  MDRout = processMUX(MAR0MUXout, mdr, 0, 0, datasize);


  /*process the MARMUX output*/
  ADDR1MUXout = processMUX(CURRENT_LATCHES.PC, CURRENT_LATCHES.REGS[SR1], 0, 0, addr1mux);
  ADDR2MUXout = processMUX(0, signExtension(ir, 6),
                              signExtension(ir, 9),
                              signExtension(ir, 11),
                              addr2mux);
  if (lshf1)
    ADDR2MUXout = ADDR2MUXout << 1;

  ADDEROut = processALU(ADDR2MUXout, ADDR1MUXout, 0);
  MARMUXout = processMUX((ir & 0xFF) << 1, ADDEROut, 0, 0, marmux);

  /*process ALU logic*/
  SR2MUXout = processMUX(CURRENT_LATCHES.REGS[SR2], signExtension(ir, 5), 0, 0, A);
  ALUout = processALU(CURRENT_LATCHES.REGS[SR1], SR2MUXout, aluk);

  /*set the Shifter output*/
  SHFout = processSHF(CURRENT_LATCHES.REGS[SR1], ir);

  /*process the Stack pointer MUX output*/
  SPMUXout = processMUX(CURRENT_LATCHES.REGS[SR1] + 2,
                        CURRENT_LATCHES.REGS[SR1] - 2,
                        CURRENT_LATCHES.SSP,
                        CURRENT_LATCHES.USP,
                        spmux);


}


void drive_bus() {

  /*
   * Datapath routine for driving the bus from one of the 5 possible
   * tristate drivers.
   */
  int gatepc     = GetGATE_PC(CURRENT_LATCHES.MICROINSTRUCTION);
  int gatepcmin2 = GetGATE_PCmin2(CURRENT_LATCHES.MICROINSTRUCTION);
  int gatealu    = GetGATE_ALU(CURRENT_LATCHES.MICROINSTRUCTION);
  int gateshf    = GetGATE_SHF(CURRENT_LATCHES.MICROINSTRUCTION);
  int gatemdr    = GetGATE_MDR(CURRENT_LATCHES.MICROINSTRUCTION);
  int gatemarmux = GetGATE_MARMUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int gatepsr    = GetGATE_PSR(CURRENT_LATCHES.MICROINSTRUCTION);
  int gatesp     = GetGATE_SP(CURRENT_LATCHES.MICROINSTRUCTION);
  int gateVec    = GetGATE_Vector(CURRENT_LATCHES.MICROINSTRUCTION);
  int mio        = GetMIO_EN(CURRENT_LATCHES.MICROINSTRUCTION);

  /*PSR variables*/
  int psr, nzp, priority, privilege;
  psr = nzp = priority = privilege = 0;

  /*do nothing while  performing mem cycle*/
  if ((mio == 1) && (CURRENT_LATCHES.READY == 0)) return;

  /*set the BUS with the current gate that is open*/
  if (gatepc == 1) {
    BUS = CURRENT_LATCHES.PC;
  }
  else if (gatepcmin2 == 1) {
    BUS = CURRENT_LATCHES.PC - 2;
  }
  else if (gatealu == 1) {
    BUS = ALUout;
  }
  else if (gateshf == 1) {
    BUS = SHFout;
  }
  else if (gatemdr == 1) {
    BUS = MDRout;
  }
  else if (gatemarmux == 1) {
    BUS = MARMUXout;
  }
  else if (gatepsr == 1){
    privilege = CURRENT_LATCHES.Priv << 15;
    priority  = CURRENT_LATCHES.priority << 8;
    nzp       = (CURRENT_LATCHES.N << 2) | (CURRENT_LATCHES.Z << 1) | CURRENT_LATCHES.P;
    psr       = privilege | priority | nzp;
    BUS = psr;
  }
  else if (gatesp == 1){
    BUS = SPMUXout;
  }
  else if (gateVec == 1){
    /*The processor left-shifts the interrupt vector one bit, yielding x02, 
      and adds it to the base address of the interrupt/exception vector table (x0200), 
      yielding the address of the memory location (x0202) that contains the 
      starting address of the interrupt service routine.*/
    BUS = Low16bits(0x0200 | ((0x00FF & CURRENT_LATCHES.Vector)<<1));
  }
  else {
    BUS = 0;
  }
}

void latch_datapath_values() {

  /*
   * Datapath routine for computing all functions that need to latch
   * values in the data path at the end of this cycle.  Some values
   * require sourcing the bus; therefore, this routine has to come
   * after drive_bus.
   */
  /*get the Load signals from the microcode*/
  int ldben     = GetLD_BEN(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldcc      = GetLD_CC(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldreg     = GetLD_REG(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldmar     = GetLD_MAR(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldmdr     = GetLD_MDR(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldir      = GetLD_IR(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldpc      = GetLD_PC(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldpriv    = GetLD_Priv(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldprio    = GetLD_Prio(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldusp     = GetLD_SavUSP(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldssp     = GetLD_SavSSP(CURRENT_LATCHES.MICROINSTRUCTION);
  int ldvector  = GetLD_Vector(CURRENT_LATCHES.MICROINSTRUCTION);
  int drmux     = GetDRMUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int datasize  = GetDATA_SIZE(CURRENT_LATCHES.MICROINSTRUCTION);
  int mio       = GetMIO_EN(CURRENT_LATCHES.MICROINSTRUCTION);
  int pcmux     = GetPCMUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int psrmux    = GetPSRMUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int vectormux = GetVectorMUX(CURRENT_LATCHES.MICROINSTRUCTION);
  int setpriv   = GetSetPriv(CURRENT_LATCHES.MICROINSTRUCTION);

  /*Get instruction and etc from the current latches*/
  int condcodes   = 0;
  int DRMUXout    = 0;
  int MDRBUSout   = 0;
  int MAR0MUXout  = 0;
  int MIOMUXout   = 0;
  int PRIVMUXout  = 0;
  int PRIOMUXout  = 0;
  int SPMUXout    = 0;
  int n  = 0, z = 0, p = 0;
  int N  = 0, Z = 0, P = 0;
  int ir = CURRENT_LATCHES.IR;

  /*PSR variables*/
  int psr,nzp,priority,privilege;
  psr = nzp = priority = privilege = 0;

  /*do nothing while  performing mem cycle*/
  if ((mio == 1) && (CURRENT_LATCHES.READY == 0)) return;

  if (ldpc) {
    NEXT_LATCHES.PC = processMUX(CURRENT_LATCHES.PC + 2, BUS, ADDEROut, 0, pcmux);
  }

  if (ldben) {
    /*get N Z P bits from the current latches*/
    N = CURRENT_LATCHES.N;
    Z = CURRENT_LATCHES.Z;
    P = CURRENT_LATCHES.P;
    /*extract nzp from instruction*/
    n = ir >> 11 & 0x0001;
    z = ir >> 10 & 0x0001;
    p = ir >> 9 & 0x0001;

    if ((n == z) && (z == p))
    {
      /*uncoditional branch*/
      NEXT_LATCHES.BEN = 1;
    }
    else {
      /*condittional branch*/
      NEXT_LATCHES.BEN = (n&N) | (z&Z) | (p&P);
    }
  }

  if (ldcc) {
    /*reset the NZP bits in the latches*/
    NEXT_LATCHES.N = NEXT_LATCHES.Z = NEXT_LATCHES.P = 0;

    if (psrmux) {
      NEXT_LATCHES.N = (BUS & 0x0004) >> 2;
      NEXT_LATCHES.Z = (BUS & 0x0002) >> 1;
      NEXT_LATCHES.P = (BUS & 0x0001);
    }
    else {
      /*get the value on the bus and check for if its - or + or 0*/
      condcodes = signExtension(BUS, 16);
      if (condcodes < 0)
        NEXT_LATCHES.N = 1;
      else if (condcodes == 0)
        NEXT_LATCHES.Z = 1;
      else if (condcodes > 0)
        NEXT_LATCHES.P = 1;
    }
  }

  if (ldreg) {
    /*get the Destination register number (0 to 8)*/
    DRMUXout = processMUX((ir & 0x0E00) >> 9, 0x07, 0x06, 0, drmux);

    /*set the destination register with the contents of the BUS*/
    NEXT_LATCHES.REGS[DRMUXout] = Low16bits(BUS);
  }

  if (ldmar) {
    /*set the MAR register with the contents of the bus*/
    NEXT_LATCHES.MAR = BUS;
  }

  if (ldmdr) {
    /*process MDR logic */
    if (datasize == 1)
      MAR0MUXout = processMUX(BUS & 0xFFFF, BUS & 0x00FF, 0, 0, CURRENT_LATCHES.MAR & 0x01);
    else
      MAR0MUXout = processMUX(BUS & 0x00FF, (BUS & 0x00FF) << 8, 0, 0, CURRENT_LATCHES.MAR & 0x01);
    
    /*mux the mdr logic with the memory*/
    NEXT_LATCHES.MDR = processMUX(MAR0MUXout, MEMORYout, 0, 0, mio);
  }

  if (ldir) {
    /*set the instruction register with the contents of the BUS*/
    NEXT_LATCHES.IR = BUS;
  }

  if (ldpriv) {
    /*set the privileg lvl of the current program running*/
    PRIVMUXout = processMUX(setpriv, (BUS & 0x7FFF), 0, 0, psrmux);
    NEXT_LATCHES.Priv = PRIVMUXout;
  }

  if (ldprio) {
    /*set the priority register based on the PSR and interupt*/
    PRIOMUXout = processMUX(INT_PRIO & 0x0007, (BUS & 0x0700) >> 8, 0, 0, psrmux);
    NEXT_LATCHES.priority = PRIOMUXout;
  }

  if(ldusp) {
    /*set the Source 1 (should be R6) register value into the USP register*/
    NEXT_LATCHES.USP = Low16bits(CURRENT_LATCHES.REGS[SR1]);
  }

  if(ldssp) {
    /*set the Source 1 (should be r6) register value into the SSP register*/
    NEXT_LATCHES.SSP = Low16bits(CURRENT_LATCHES.REGS[SR1]);
  }

  if (ldvector) {
    CURRENT_LATCHES.INTV = 0x01;
    /*load the interupt/exception vector into the vector base register*/
    /*or its a interupt or its an exception*/
    NEXT_LATCHES.Vector = 0x00FF & processMUX(CURRENT_LATCHES.INTV, 
                                              0x02, 0x03, 0x04, 
                                              vectormux);
  }

  /*always save the state of the PSR whenver
    there is a change on the PSR bits*/
  if (ldpriv || ldprio || ldcc) {
    nzp = (NEXT_LATCHES.N << 2) | (NEXT_LATCHES.Z << 1) | (NEXT_LATCHES.P);
    priority = NEXT_LATCHES.priority << 8;
    privilege = NEXT_LATCHES.Priv << 15;
    psr = privilege | priority | nzp;
    NEXT_LATCHES.PSR = Low16bits(psr);
  }
}
